Il primo step sarà quello di installare tutti i pacchetti necessari:

if (!require("BiocManager", quietly = TRUE))
    install.packages("BiocManager")

BiocManager::install("curatedTCGAData")
BiocManager::install("TCGAutils")
BiocManager::install("TCGAbiolinks")
install.packages("SNFtool")
install.packages("NetPreProc")
install.packages("caret");
install.packages("cluster");
install.packages("mclustcomp");

quindi carico le librerie:

library("curatedTCGAData");
library("TCGAbiolinks");
library("TCGAutils");
library("SNFtool");
library("NetPreProc");
library("caret");
library("cluster"); #pam
library("mclustcomp");

A questo punto per ottenere il dataset del carcinoma prostatico (PRAD) contenente informazioni su espressione genica a livello di mRNA, espressione di microRNA e espressione proteica, ho seguito il seguente processo:

# ho specificato le omiche di interesse richieste dal progetto
assays <- c("miRNASeqGene", "RNASeq2Gene", "RPPAArray");

# ottengo i dati per il dataset del carcinoma prostatico
mo <- curatedTCGAData(diseaseCode = "PRAD", 
                        assays = assays, 
                        version = "2.0.1", dry.run = FALSE);
snapshotDate(): 2023-10-24
Working on: PRAD_miRNASeqGene-20160128
see ?curatedTCGAData and browseVignettes('curatedTCGAData') for documentation
loading from cache
Working on: PRAD_RNASeq2Gene-20160128
see ?curatedTCGAData and browseVignettes('curatedTCGAData') for documentation
loading from cache
Working on: PRAD_RPPAArray-20160128
see ?curatedTCGAData and browseVignettes('curatedTCGAData') for documentation
loading from cache
Working on: PRAD_colData-20160128
see ?curatedTCGAData and browseVignettes('curatedTCGAData') for documentation
loading from cache
Working on: PRAD_metadata-20160128
see ?curatedTCGAData and browseVignettes('curatedTCGAData') for documentation
loading from cache
Working on: PRAD_sampleMap-20160128
see ?curatedTCGAData and browseVignettes('curatedTCGAData') for documentation
loading from cache
harmonizing input:
  removing 5189 sampleMap rows not in names(experiments)
mo
A MultiAssayExperiment object of 3 listed
 experiments with user-defined names and respective classes.
 Containing an ExperimentList class object of length 3:
 [1] PRAD_miRNASeqGene-20160128: SummarizedExperiment with 1046 rows and 547 columns
 [2] PRAD_RNASeq2Gene-20160128: SummarizedExperiment with 20501 rows and 550 columns
 [3] PRAD_RPPAArray-20160128: SummarizedExperiment with 195 rows and 352 columns
Functionality:
 experiments() - obtain the ExperimentList instance
 colData() - the primary/phenotype DataFrame
 sampleMap() - the sample coordination DataFrame
 `$`, `[`, `[[` - extract colData columns, subset, or experiment
 *Format() - convert into a long or wide DataFrame
 assays() - convert ExperimentList to a SimpleList of matrices
 exportClass() - save data to flat files

Fase 2: Data pre-processing Ogni campione è identificato da un barcode con una specifica struttura: - i primi 12 caratteri identificano uno specifico individuo - le altre parti ci danno indicazioni sul tipo di campione (primario, metastatico, solido, derivato dal sangue, ecc.), sul tipo di materiale genomico estratto (DNA, RNA) e altre informazioni relative alle repliche tecniche (cioè misurazioni ripetute dallo stesso campione).

Usiamo il barcode per: - conservare solo i tumori solidi primari per avere un gruppo di campioni più omogeno. Questo è identificato dal codice “01” nella parte “sample” del barcode - Rimuovere eventuali duplicati rappresentati da campioni che hanno gli stessi 12 caratteri iniziali

# Considerare solo i tumori solidi primari:
primary <- TCGAutils::TCGAsampleSelect(colnames(mo), c("01"))
Avvertimento: Inconsistent barcode lengths: 28, 27Avvertimento: Inconsistent barcode lengths: 28, 27
mo <- mo[, primary, ]
harmonizing input:
  removing 106 sampleMap rows with 'colname' not in colnames of experiments
# Controllo delle repliche 
check_rep <- anyReplicated(mo)
print(check_rep)
PRAD_miRNASeqGene-20160128  PRAD_RNASeq2Gene-20160128    PRAD_RPPAArray-20160128 
                     FALSE                      FALSE                      FALSE 
# Verifica se ci sono valori nulli in ciascuna colonna di un dataframe
for (i in 1:length(complete)){
  print(paste(names(complete)[i], ":", any(colSums(is.na(complete[[i]])) > 0)))
}
[1] "PRAD_miRNASeqGene-20160128 : FALSE"
[1] "PRAD_RNASeq2Gene-20160128 : FALSE"
[1] "PRAD_RPPAArray-20160128 : FALSE"
# Remove features having NAs (present only in proteomics data):
complete[[3]] <- complete[[3]][, colSums(is.na(complete[[3]])) == 0];

# Remove features with near zero variance and retain top 500 features 
# having higher variance:
nf <- 100;
for(i in 1:length(complete)){
    
    idx <- caret::nearZeroVar(complete[[i]])
    message(paste("Removed ", length(idx), "features from", names(complete)[i]));
    if(length(idx) != 0){
        complete[[i]] <- complete[[i]][, -idx];
    }

    if(ncol(complete[[i]]) <= nf) next
    
    vars <- apply(complete[[i]], 2, var);
    idx <- sort(vars, index.return=TRUE, decreasing = TRUE)$ix;
    
    complete[[i]] <- complete[[i]][, idx[1:nf]];
    as.data.frame(complete[[i]])
    
}
Removed  0 features from PRAD_miRNASeqGene-20160128
Removed  1334 features from PRAD_RNASeq2Gene-20160128
Removed  0 features from PRAD_RPPAArray-20160128
# Perform features standardization using z-score:
zscore <- function(data){
    
    zscore_vec <- function(x) { return ((x - mean(x)) / sd(x))}
    data <- apply(data, 2, zscore_vec)
    
    
    return(data)
}

complete <- lapply(complete, zscore);

# Clean barcodes retaining only "Project-TSS-Participant":
for(v in 1:length(complete)){
    rownames(complete[[v]]) <- substr(rownames(complete[[v]]), 1, 12);
}

PUNTO 3:

# Print number of samples for each subtype:
table(subtypes$Subtype_Integrative);

  1   2   3 
 60  83 105 
# Verifica che i pazienti nei dataset multi-omics e nelle sottocategorie siano nello stesso ordine
matching_order <- all(!is.na(match(rownames(complete[[1]]), substr(subtypes$pan.samplesID, 1, 12))))

if (matching_order) {
  cat("I pazienti nei dataset multi-omics e nelle sottocategorie sono nello stesso ordine.\n")
} else {
  cat("ATTENZIONE: I pazienti nei dataset multi-omics e nelle sottocategorie NON sono nello stesso ordine.\n")
}
I pazienti nei dataset multi-omics e nelle sottocategorie sono nello stesso ordine.

PUNTO 4:

matching_order <- TRUE

for (i in 1:length(complete)) {
  matching_order <- matching_order & !is.na(match(rownames(complete[[i]]), substr(subtypes$pan.samplesID, 1, 12)))
}

if (all(matching_order)) {
  cat("I pazienti nei dataset multi-omics e nelle sottocategorie sono nello stesso ordine dopo il riordinamento.\n")
} else {
  cat("ATTENZIONE: I pazienti nei dataset multi-omics e nelle sottocategorie NON sono nello stesso ordine.\n")
}
I pazienti nei dataset multi-omics e nelle sottocategorie sono nello stesso ordine dopo il riordinamento.
K = 20; # Number of neighbors
T = 20; # Number of iterations

# Compute similarity matrix for each data source using the scaled
# exponential euclidean distance:
W_list <- list();
for(i in 1:length(complete)){
    Dist <- (dist2(as.matrix(complete[[i]]), as.matrix(complete[[i]])))^(1/2);
    W_list[[i]] <- affinityMatrix(Dist);
}
    
# Integration of multi-omics data using Similarity Network Fusion:
# t is the number of iterations and K is the number of neighbors to 
# consider to compute the local similarity matrix:
W_int <- SNF(W_list, K, T)

PUNTO 6:

# Calcolo della media delle matrici di similarità
W_average <- Reduce(`+`, W_list) / length(W_list)

PUNTO 8a:

# b. Normalizza le matrici di similarità
for (i in 1:length(W_list)) {
    W_list[[i]] <- NetPreProc::Max.Min.norm(W_list[[i]])
}

# c. Converti le matrici di similarità normalizzate in matrici di distanza
D_list_single <- lapply(W_list, function(W) 1 - NetPreProc::Max.Min.norm(W))

# Esegui PAM sugli insiemi di dati integrati
k <- length(unique(subtypes$Subtype_Selected))

# PAM per miRNA
pam_res_miRNA <- pam(as.dist(D_list_single[[1]]), k = k)

# PAM per mRNA
pam_res_mRNA <- pam(as.dist(D_list_single[[2]]), k = k)

# PAM per proteine
pam_res_proteins <- pam(as.dist(D_list_single[[3]]), k = k)

PUNTO 8b:

# Normalizza la matrice di similarità
W_avg_norm <- NetPreProc::Max.Min.norm(W_average)

# Calcola la matrice di distanza
D_avg <- as.dist(1 - W_avg_norm)

# Esegui PAM sulla matrice integrata ottenuta dalla media
pam_res_avg <- pam(D_avg, k = k)

PUNTO 8C:

# Normalizza la matrice di similarità fusionata
W_int_norm <- NetPreProc::Max.Min.norm(W_int)

# Calcola la matrice di distanza
D_int <- as.dist(1 - W_int_norm)

# Applica PAM sull'insieme di dati integrato tramite Similarity Network Fusion
pam_res_snf <- pam(D_int, k = k)

PUNTO 10 (Opzionale):

spectral_clustering <- spectralClustering(W_int, k)
str(spectral_clustering)
 int [1:248] 1 1 3 1 3 3 2 2 2 2 ...
# Visualizza i risultati
table(spectral_clustering)
spectral_clustering
 1  2  3 
83 84 81 
clustering_results <- list(pam_res_miRNA = pam_res_miRNA$clustering,
                           pam_res_mRNA = pam_res_mRNA$clustering,
                           pam_res_proteins = pam_res_proteins$clustering,
                           pam_res_avg = pam_res_avg$clustering,
                           pam_res_snf = pam_res_snf$clustering,
                           spectral_clustering = spectral_clustering)

# Calcolare la tabella di contingenza per ciascun risultato di clustering rispetto ai sottotipi di malattie iCluster
for (name in names(clustering_results)) {
  print(paste("Contingency table for", name))
  print(table(subtypes$Subtype_Integrative, clustering_results[[name]]))
}
[1] "Contingency table for pam_res_miRNA"
   
     1  2  3
  1 24 15 21
  2 51 23  9
  3 59 30 16
[1] "Contingency table for pam_res_mRNA"
   
     1  2  3
  1 15 18 27
  2  7 26 50
  3 42 18 45
[1] "Contingency table for pam_res_proteins"
   
     1  2  3
  1 24 23 13
  2 38 32 13
  3 33 35 37
[1] "Contingency table for pam_res_avg"
   
     1  2  3
  1 29 25  6
  2 37 24 22
  3 25 44 36
[1] "Contingency table for pam_res_snf"
   
     1  2  3
  1 14  9 37
  2 50 18 15
  3 14 65 26
[1] "Contingency table for spectral_clustering"
   
     1  2  3
  1 15 16 29
  2 12 28 43
  3 56 40  9
# Calcolare l'indice di Rand Adjusted per ciascun risultato di clustering rispetto ai sottotipi di malattie iCluster
for (name in names(clustering_results)) {
  print(paste("Adjusted Rand index for", name))
  print(cluster::adjustedRandIndex(iCluster_subtypes, clustering_results[[name]]))
}
[1] "Adjusted Rand index for pam_res_miRNA"
Errore: 'adjustedRandIndex' non è un oggetto esportato dal 'namespace:cluster'

PUNTO 11:

# Creare una tabella di confronto con nomi descrittivi per gli approcci
comparison_table <- data.frame(
  Method = c("PAM_miRNA", "PAM_mRNA", "PAM_proteins", "PAM_Avg", "PAM_SNF", "Spectral_Clustering"),
  Cluster_Assignment = c(
    pam_res_miRNA$clustering, 
    pam_res_mRNA$clustering, 
    pam_res_proteins$clustering, 
    pam_res_avg$clustering, 
    pam_res_snf$clustering, 
    spectral_clustering
  )
)

# Visualizzare la tabella
print(comparison_table)

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpJbCBwcmltbyBzdGVwIHNhcsOgIHF1ZWxsbyBkaSBpbnN0YWxsYXJlIHR1dHRpIGkgcGFjY2hldHRpIG5lY2Vzc2FyaToNCg0KYGBge3J9DQppZiAoIXJlcXVpcmUoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCg0KQmlvY01hbmFnZXI6Omluc3RhbGwoImN1cmF0ZWRUQ0dBRGF0YSIpDQpCaW9jTWFuYWdlcjo6aW5zdGFsbCgiVENHQXV0aWxzIikNCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJUQ0dBYmlvbGlua3MiKQ0KaW5zdGFsbC5wYWNrYWdlcygiU05GdG9vbCIpDQppbnN0YWxsLnBhY2thZ2VzKCJOZXRQcmVQcm9jIikNCmluc3RhbGwucGFja2FnZXMoImNhcmV0Iik7DQppbnN0YWxsLnBhY2thZ2VzKCJjbHVzdGVyIik7DQppbnN0YWxsLnBhY2thZ2VzKCJtY2x1c3Rjb21wIik7DQpgYGANCg0KcXVpbmRpIGNhcmljbyBsZSBsaWJyZXJpZToNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoImN1cmF0ZWRUQ0dBRGF0YSIpOw0KbGlicmFyeSgiVENHQWJpb2xpbmtzIik7DQpsaWJyYXJ5KCJUQ0dBdXRpbHMiKTsNCmxpYnJhcnkoIlNORnRvb2wiKTsNCmxpYnJhcnkoIk5ldFByZVByb2MiKTsNCmxpYnJhcnkoImNhcmV0Iik7DQpsaWJyYXJ5KCJjbHVzdGVyIik7ICNwYW0NCmxpYnJhcnkoIm1jbHVzdGNvbXAiKTsNCmBgYA0KDQpBIHF1ZXN0byBwdW50byBwZXIgb3R0ZW5lcmUgaWwgZGF0YXNldCBkZWwgY2FyY2lub21hIHByb3N0YXRpY28gKFBSQUQpIGNvbnRlbmVudGUgaW5mb3JtYXppb25pIHN1IGVzcHJlc3Npb25lIGdlbmljYSBhIGxpdmVsbG8gZGkgbVJOQSwgZXNwcmVzc2lvbmUgZGkgbWljcm9STkEgZSBlc3ByZXNzaW9uZSBwcm90ZWljYSwgaG8gc2VndWl0byBpbCBzZWd1ZW50ZSBwcm9jZXNzbzoNCg0KYGBge3IgbWVzc2FnZT1UUlVFfQ0KIyBobyBzcGVjaWZpY2F0byBsZSBvbWljaGUgZGkgaW50ZXJlc3NlIHJpY2hpZXN0ZSBkYWwgcHJvZ2V0dG8NCmFzc2F5cyA8LSBjKCJtaVJOQVNlcUdlbmUiLCAiUk5BU2VxMkdlbmUiLCAiUlBQQUFycmF5Iik7DQoNCiMgb3R0ZW5nbyBpIGRhdGkgcGVyIGlsIGRhdGFzZXQgZGVsIGNhcmNpbm9tYSBwcm9zdGF0aWNvDQptbyA8LSBjdXJhdGVkVENHQURhdGEoZGlzZWFzZUNvZGUgPSAiUFJBRCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXlzID0gYXNzYXlzLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHZlcnNpb24gPSAiMi4wLjEiLCBkcnkucnVuID0gRkFMU0UpOw0KbW8NCmBgYA0KDQpGYXNlIDI6IERhdGEgcHJlLXByb2Nlc3NpbmcNCk9nbmkgY2FtcGlvbmUgw6ggaWRlbnRpZmljYXRvIGRhIHVuIGJhcmNvZGUgY29uIHVuYSBzcGVjaWZpY2Egc3RydXR0dXJhOg0KICAtIGkgcHJpbWkgMTIgY2FyYXR0ZXJpIGlkZW50aWZpY2FubyB1bm8gc3BlY2lmaWNvIGluZGl2aWR1byANCiAgLSBsZSBhbHRyZSBwYXJ0aSBjaSBkYW5ubyBpbmRpY2F6aW9uaSBzdWwgdGlwbyBkaSBjYW1waW9uZSAocHJpbWFyaW8sIG1ldGFzdGF0aWNvLCBzb2xpZG8sIGRlcml2YXRvIGRhbCBzYW5ndWUsIGVjYy4pLCBzdWwgdGlwbyBkaSBtYXRlcmlhbGUgZ2Vub21pY28gZXN0cmF0dG8gKEROQSwgUk5BKSBlIGFsdHJlIGluZm9ybWF6aW9uaSByZWxhdGl2ZSBhbGxlIHJlcGxpY2hlIHRlY25pY2hlIChjaW/DqCBtaXN1cmF6aW9uaSByaXBldHV0ZSBkYWxsbyBzdGVzc28gY2FtcGlvbmUpLg0KDQpVc2lhbW8gaWwgYmFyY29kZSBwZXI6DQogIC0gY29uc2VydmFyZSBzb2xvIGkgdHVtb3JpIHNvbGlkaSBwcmltYXJpIHBlciBhdmVyZSB1biBncnVwcG8gZGkgY2FtcGlvbmkgcGnDuSBvbW9nZW5vLiBRdWVzdG8gw6ggaWRlbnRpZmljYXRvIGRhbCBjb2RpY2UgIjAxIiBuZWxsYSBwYXJ0ZSAic2FtcGxlIiBkZWwgYmFyY29kZQ0KICAtIFJpbXVvdmVyZSBldmVudHVhbGkgZHVwbGljYXRpIHJhcHByZXNlbnRhdGkgZGEgY2FtcGlvbmkgY2hlIGhhbm5vIGdsaSBzdGVzc2kgMTIgY2FyYXR0ZXJpIGluaXppYWxpDQpgYGB7cn0NCiMgQ29uc2lkZXJhcmUgc29sbyBpIHR1bW9yaSBzb2xpZGkgcHJpbWFyaToNCnByaW1hcnkgPC0gVENHQXV0aWxzOjpUQ0dBc2FtcGxlU2VsZWN0KGNvbG5hbWVzKG1vKSwgYygiMDEiKSkNCm1vIDwtIG1vWywgcHJpbWFyeSwgXQ0KDQojIENvbnRyb2xsbyBkZWxsZSByZXBsaWNoZSANCmNoZWNrX3JlcCA8LSBhbnlSZXBsaWNhdGVkKG1vKQ0KcHJpbnQoY2hlY2tfcmVwKQ0KYGBgDQpgYGB7cn0NCiMgVGhlIGluZm9ybWF0aW9uIHJlZ2FyZGluZyBpZiB0aGUgc2FtcGxlIGlzIEZGUEUgaXMgc3RvcmVkIGluIHRoZSBjbGluaWNhbCBkYXRhLA0KIyB3aGljaCBhcmUgYWNjZXNzaWJsZSB1c2luZyBjb2xEYXRhKCkuIA0Kbm9fZmZwZSA8LSB3aGljaChhcy5kYXRhLmZyYW1lKGNvbERhdGEobW8pKSRwYXRpZW50LnNhbXBsZXMuc2FtcGxlLmlzX2ZmcGUgPT0gIm5vIik7DQptbyA8LSBtb1ssIG5vX2ZmcGUsIF07DQoNCiMgT2J0YWluIHNhbXBsZXMgaGF2aW5nIGFsbCB0aGUgY29uc2lkZXJlZCBvbWljczoNCmNvbXBsZXRlIDwtIGludGVyc2VjdENvbHVtbnMobW8pOw0KIyBFeHRyYWN0IGFzc2F5cyBpbiBsaXN0Og0KY29tcGxldGUgPC0gYXNzYXlzKGNvbXBsZXRlKTsNCiMgT2J0YWluIG1hdHJpY2VzIHNhbXBsZXMgeCBmZWF0dXJlczoNCmNvbXBsZXRlIDwtIGxhcHBseShjb21wbGV0ZSwgRlVOPXQpDQojY29sbmFtZXMoY29tcGxldGVbWzJdXSkNCmBgYA0KDQoNCg0KYGBge3J9DQojIFZlcmlmaWNhIHNlIGNpIHNvbm8gdmFsb3JpIG51bGxpIGluIGNpYXNjdW5hIGNvbG9ubmEgZGkgdW4gZGF0YWZyYW1lDQpmb3IgKGkgaW4gMTpsZW5ndGgoY29tcGxldGUpKXsNCiAgcHJpbnQocGFzdGUobmFtZXMoY29tcGxldGUpW2ldLCAiOiIsIGFueShjb2xTdW1zKGlzLm5hKGNvbXBsZXRlW1tpXV0pKSA+IDApKSkNCn0NCg0KIyBSZW1vdmUgZmVhdHVyZXMgaGF2aW5nIE5BcyAocHJlc2VudCBvbmx5IGluIHByb3Rlb21pY3MgZGF0YSk6DQpjb21wbGV0ZVtbM11dIDwtIGNvbXBsZXRlW1szXV1bLCBjb2xTdW1zKGlzLm5hKGNvbXBsZXRlW1szXV0pKSA9PSAwXTsNCg0KIyBSZW1vdmUgZmVhdHVyZXMgd2l0aCBuZWFyIHplcm8gdmFyaWFuY2UgYW5kIHJldGFpbiB0b3AgNTAwIGZlYXR1cmVzIA0KIyBoYXZpbmcgaGlnaGVyIHZhcmlhbmNlOg0KbmYgPC0gMTAwOw0KZm9yKGkgaW4gMTpsZW5ndGgoY29tcGxldGUpKXsNCiAgICANCiAgICBpZHggPC0gY2FyZXQ6Om5lYXJaZXJvVmFyKGNvbXBsZXRlW1tpXV0pDQogICAgbWVzc2FnZShwYXN0ZSgiUmVtb3ZlZCAiLCBsZW5ndGgoaWR4KSwgImZlYXR1cmVzIGZyb20iLCBuYW1lcyhjb21wbGV0ZSlbaV0pKTsNCiAgICBpZihsZW5ndGgoaWR4KSAhPSAwKXsNCiAgICAgICAgY29tcGxldGVbW2ldXSA8LSBjb21wbGV0ZVtbaV1dWywgLWlkeF07DQogICAgfQ0KDQogICAgaWYobmNvbChjb21wbGV0ZVtbaV1dKSA8PSBuZikgbmV4dA0KICAgIA0KICAgIHZhcnMgPC0gYXBwbHkoY29tcGxldGVbW2ldXSwgMiwgdmFyKTsNCiAgICBpZHggPC0gc29ydCh2YXJzLCBpbmRleC5yZXR1cm49VFJVRSwgZGVjcmVhc2luZyA9IFRSVUUpJGl4Ow0KICAgIA0KICAgIGNvbXBsZXRlW1tpXV0gPC0gY29tcGxldGVbW2ldXVssIGlkeFsxOm5mXV07DQogICAgDQp9DQoNCiMgUGVyZm9ybSBmZWF0dXJlcyBzdGFuZGFyZGl6YXRpb24gdXNpbmcgei1zY29yZToNCnpzY29yZSA8LSBmdW5jdGlvbihkYXRhKXsNCiAgICANCiAgICB6c2NvcmVfdmVjIDwtIGZ1bmN0aW9uKHgpIHsgcmV0dXJuICgoeCAtIG1lYW4oeCkpIC8gc2QoeCkpfQ0KICAgIGRhdGEgPC0gYXBwbHkoZGF0YSwgMiwgenNjb3JlX3ZlYykNCiAgICANCiAgICANCiAgICByZXR1cm4oZGF0YSkNCn0NCg0KY29tcGxldGUgPC0gbGFwcGx5KGNvbXBsZXRlLCB6c2NvcmUpOw0KDQojIENsZWFuIGJhcmNvZGVzIHJldGFpbmluZyBvbmx5ICJQcm9qZWN0LVRTUy1QYXJ0aWNpcGFudCI6DQpmb3IodiBpbiAxOmxlbmd0aChjb21wbGV0ZSkpew0KICAgIHJvd25hbWVzKGNvbXBsZXRlW1t2XV0pIDwtIHN1YnN0cihyb3duYW1lcyhjb21wbGV0ZVtbdl1dKSwgMSwgMTIpOw0KfQ0KYGBgDQoNClBVTlRPIDM6DQpgYGB7cn0NCiMgRG93bmxvYWQgZGlzZWFzZSBzdWJ0eXBlcyBmcm9tIFRDR0FiaW9saW5rczoNCnN1YnR5cGVzIDwtIGFzLmRhdGEuZnJhbWUoVENHQWJpb2xpbmtzOjpQYW5DYW5jZXJBdGxhc19zdWJ0eXBlcygpKTsNCnN1YnR5cGVzIDwtIHN1YnR5cGVzW3N1YnR5cGVzJGNhbmNlci50eXBlID09ICJQUkFEIiwgXTsNCiMgUmV0YWluIG9ubHkgcHJpbWFyeSBzb2xpZCB0dW1vcnMgYW5kIHNlbGVjdCBzYW1wbGVzIGluIGNvbW1vbiB3aXRoIG9taWNzIGRhdGENCiMgKGluIHRoZSBzYW1lIG9yZGVyKToNCnN1YnR5cGVzIDwtIHN1YnR5cGVzW1RDR0F1dGlsczo6VENHQXNhbXBsZVNlbGVjdChzdWJ0eXBlcyRwYW4uc2FtcGxlc0lELCAiMDEiKSwgXTsNCnN1Yl9zZWxlY3QgPC0gc3Vic3RyKHN1YnR5cGVzJHBhbi5zYW1wbGVzSUQsMSwxMikgJWluJSByb3duYW1lcyhjb21wbGV0ZVtbMV1dKTsNCnN1YnR5cGVzIDwtIHN1YnR5cGVzW3N1Yl9zZWxlY3QsIF07DQpyb3duYW1lcyhzdWJ0eXBlcykgPC0gc3Vic3RyKHN1YnR5cGVzJHBhbi5zYW1wbGVzSUQsIDEsIDEyKTsNCnN1YnR5cGVzIDwtIHN1YnR5cGVzW3Jvd25hbWVzKGNvbXBsZXRlW1sxXV0pLF07DQoNCiMgUHJpbnQgbnVtYmVyIG9mIHNhbXBsZXMgZm9yIGVhY2ggc3VidHlwZToNCnRhYmxlKHN1YnR5cGVzJFN1YnR5cGVfSW50ZWdyYXRpdmUpOw0KDQojIFJpbXVvdmkgbGUgcmlnaGUgY29uIE5BIG5lbGxhIGNvbG9ubmEgcGFuLnNhbXBsZXNJRA0Kc3VidHlwZXMgPC0gc3VidHlwZXNbIWlzLm5hKHN1YnR5cGVzJHBhbi5zYW1wbGVzSUQpLCBdDQoNCmZvciAoaSBpbiAxOmxlbmd0aChjb21wbGV0ZSkpIHsNCiMgU2VsZXppb25hIHNvbG8gbGUgcmlnaGUgZGkgY29tcGxldGVbW2ldXSBjb3JyaXNwb25kZW50aSBhIHBhemllbnRpIGluIHN1YnR5cGVzDQpjb21wbGV0ZVtbaV1dIDwtIGNvbXBsZXRlW1tpXV1bcm93bmFtZXMoY29tcGxldGVbW2ldXSkgJWluJSByb3duYW1lcyhzdWJ0eXBlcyksIF0NCn0NCg0KI2FzLmRhdGEuZnJhbWUoc3VidHlwZXMpDQojYXMuZGF0YS5mcmFtZShjb21wbGV0ZVtbMV1dKQ0KYGBgDQoNCmBgYHtyfQ0KIyBWZXJpZmljYSBjaGUgaSBwYXppZW50aSBuZWkgZGF0YXNldCBtdWx0aS1vbWljcyBlIG5lbGxlIHNvdHRvY2F0ZWdvcmllIHNpYW5vIG5lbGxvIHN0ZXNzbyBvcmRpbmUNCiNtYXRjaGluZ19vcmRlciA8LSBhbGwoIWlzLm5hKG1hdGNoKHJvd25hbWVzKGNvbXBsZXRlW1sxXV0pLCBzdWJzdHIoc3VidHlwZXMkcGFuLnNhbXBsZXNJRCwgMSwgMTIpKSkpDQoNCiNpZiAobWF0Y2hpbmdfb3JkZXIpIHsNCiMgIGNhdCgiSSBwYXppZW50aSBuZWkgZGF0YXNldCBtdWx0aS1vbWljcyBlIG5lbGxlIHNvdHRvY2F0ZWdvcmllIHNvbm8gbmVsbG8gc3Rlc3NvIG9yZGluZS5cbiIpDQojfSBlbHNlIHsNCiMgIGNhdCgiQVRURU5aSU9ORTogSSBwYXppZW50aSBuZWkgZGF0YXNldCBtdWx0aS1vbWljcyBlIG5lbGxlIHNvdHRvY2F0ZWdvcmllIE5PTiBzb25vIG5lbGxvIHN0ZXNzbyBvcmRpbmUuXG4iKQ0KI30NCmBgYA0KDQoNClBVTlRPIDQ6DQpgYGB7cn0NCiMgVmVyaWZpY2EgY2hlIGkgcGF6aWVudGkgbmVpIGRhdGFzZXQgbXVsdGktb21pY3MgZSBuZWxsZSBzb3R0b2NhdGVnb3JpZSBzaWFubyBuZWxsbyBzdGVzc28gb3JkaW5lIHBlciB0dXR0aSBnbGkgb21pY2kNCm1hdGNoaW5nX29yZGVyIDwtIFRSVUUNCg0KZm9yIChpIGluIDE6bGVuZ3RoKGNvbXBsZXRlKSkgew0KICBtYXRjaGluZ19vcmRlciA8LSBtYXRjaGluZ19vcmRlciAmICFpcy5uYShtYXRjaChyb3duYW1lcyhjb21wbGV0ZVtbaV1dKSwgc3Vic3RyKHN1YnR5cGVzJHBhbi5zYW1wbGVzSUQsIDEsIDEyKSkpDQp9DQoNCmlmIChhbGwobWF0Y2hpbmdfb3JkZXIpKSB7DQogIGNhdCgiSSBwYXppZW50aSBuZWkgZGF0YXNldCBtdWx0aS1vbWljcyBlIG5lbGxlIHNvdHRvY2F0ZWdvcmllIHNvbm8gbmVsbG8gc3Rlc3NvIG9yZGluZSBkb3BvIGlsIHJpb3JkaW5hbWVudG8uXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCJBVFRFTlpJT05FOiBJIHBhemllbnRpIG5laSBkYXRhc2V0IG11bHRpLW9taWNzIGUgbmVsbGUgc290dG9jYXRlZ29yaWUgTk9OIHNvbm8gbmVsbG8gc3Rlc3NvIG9yZGluZS5cbiIpDQp9DQpgYGANCg0KDQpgYGB7cn0NCksgPSAyMDsgIyBOdW1iZXIgb2YgbmVpZ2hib3JzDQpUID0gMjA7ICMgTnVtYmVyIG9mIGl0ZXJhdGlvbnMNCg0KIyBDb21wdXRlIHNpbWlsYXJpdHkgbWF0cml4IGZvciBlYWNoIGRhdGEgc291cmNlIHVzaW5nIHRoZSBzY2FsZWQNCiMgZXhwb25lbnRpYWwgZXVjbGlkZWFuIGRpc3RhbmNlOg0KV19saXN0IDwtIGxpc3QoKTsNCmZvcihpIGluIDE6bGVuZ3RoKGNvbXBsZXRlKSl7DQogICAgRGlzdCA8LSAoZGlzdDIoYXMubWF0cml4KGNvbXBsZXRlW1tpXV0pLCBhcy5tYXRyaXgoY29tcGxldGVbW2ldXSkpKV4oMS8yKTsNCiAgICBXX2xpc3RbW2ldXSA8LSBhZmZpbml0eU1hdHJpeChEaXN0KTsNCn0NCiAgICANCiMgSW50ZWdyYXRpb24gb2YgbXVsdGktb21pY3MgZGF0YSB1c2luZyBTaW1pbGFyaXR5IE5ldHdvcmsgRnVzaW9uOg0KIyB0IGlzIHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucyBhbmQgSyBpcyB0aGUgbnVtYmVyIG9mIG5laWdoYm9ycyB0byANCiMgY29uc2lkZXIgdG8gY29tcHV0ZSB0aGUgbG9jYWwgc2ltaWxhcml0eSBtYXRyaXg6DQpXX2ludCA8LSBTTkYoV19saXN0LCBLLCBUKQ0KDQpgYGANCg0KDQpQVU5UTyA2OiANCmBgYHtyfQ0KIyBDYWxjb2xvIGRlbGxhIG1lZGlhIGRlbGxlIG1hdHJpY2kgZGkgc2ltaWxhcml0w6ANCldfYXZlcmFnZSA8LSBSZWR1Y2UoYCtgLCBXX2xpc3QpIC8gbGVuZ3RoKFdfbGlzdCkNCmBgYA0KDQoNClBVTlRPIDhhOg0KYGBge3J9DQojIGIuIE5vcm1hbGl6emEgbGUgbWF0cmljaSBkaSBzaW1pbGFyaXTDoA0KZm9yIChpIGluIDE6bGVuZ3RoKFdfbGlzdCkpIHsNCiAgICBXX2xpc3RbW2ldXSA8LSBOZXRQcmVQcm9jOjpNYXguTWluLm5vcm0oV19saXN0W1tpXV0pDQp9DQoNCiMgYy4gQ29udmVydGkgbGUgbWF0cmljaSBkaSBzaW1pbGFyaXTDoCBub3JtYWxpenphdGUgaW4gbWF0cmljaSBkaSBkaXN0YW56YQ0KRF9saXN0X3NpbmdsZSA8LSBsYXBwbHkoV19saXN0LCBmdW5jdGlvbihXKSAxIC0gTmV0UHJlUHJvYzo6TWF4Lk1pbi5ub3JtKFcpKQ0KDQojIEVzZWd1aSBQQU0gc3VnbGkgaW5zaWVtaSBkaSBkYXRpIGludGVncmF0aQ0KayA8LSBsZW5ndGgodW5pcXVlKHN1YnR5cGVzJFN1YnR5cGVfSW50ZWdyYXRpdmUpKQ0KDQojIFBBTSBwZXIgbWlSTkENCnBhbV9yZXNfbWlSTkEgPC0gcGFtKGFzLmRpc3QoRF9saXN0X3NpbmdsZVtbMV1dKSwgayA9IGspDQoNCiMgUEFNIHBlciBtUk5BDQpwYW1fcmVzX21STkEgPC0gcGFtKGFzLmRpc3QoRF9saXN0X3NpbmdsZVtbMl1dKSwgayA9IGspDQoNCiMgUEFNIHBlciBwcm90ZWluZQ0KcGFtX3Jlc19wcm90ZWlucyA8LSBwYW0oYXMuZGlzdChEX2xpc3Rfc2luZ2xlW1szXV0pLCBrID0gaykNCmBgYA0KDQoNClBVTlRPIDhiOg0KYGBge3J9DQojIE5vcm1hbGl6emEgbGEgbWF0cmljZSBkaSBzaW1pbGFyaXTDoA0KV19hdmdfbm9ybSA8LSBOZXRQcmVQcm9jOjpNYXguTWluLm5vcm0oV19hdmVyYWdlKQ0KDQojIENhbGNvbGEgbGEgbWF0cmljZSBkaSBkaXN0YW56YQ0KRF9hdmcgPC0gYXMuZGlzdCgxIC0gV19hdmdfbm9ybSkNCg0KIyBFc2VndWkgUEFNIHN1bGxhIG1hdHJpY2UgaW50ZWdyYXRhIG90dGVudXRhIGRhbGxhIG1lZGlhDQpwYW1fcmVzX2F2ZyA8LSBwYW0oRF9hdmcsIGsgPSBrKQ0KYGBgDQoNCg0KUFVOVE8gOEM6DQpgYGB7cn0NCiMgTm9ybWFsaXp6YSBsYSBtYXRyaWNlIGRpIHNpbWlsYXJpdMOgIGZ1c2lvbmF0YQ0KV19pbnRfbm9ybSA8LSBOZXRQcmVQcm9jOjpNYXguTWluLm5vcm0oV19pbnQpDQoNCiMgQ2FsY29sYSBsYSBtYXRyaWNlIGRpIGRpc3RhbnphDQpEX2ludCA8LSBhcy5kaXN0KDEgLSBXX2ludF9ub3JtKQ0KDQojIEFwcGxpY2EgUEFNIHN1bGwnaW5zaWVtZSBkaSBkYXRpIGludGVncmF0byB0cmFtaXRlIFNpbWlsYXJpdHkgTmV0d29yayBGdXNpb24NCnBhbV9yZXNfc25mIDwtIHBhbShEX2ludCwgayA9IGspDQpgYGANCg0KDQpQVU5UTyAxMCAoT3B6aW9uYWxlKToNCmBgYHtyfQ0KIyBFc2VndWkgaWwgY2x1c3RlcmluZyBzcGV0dHJhbGUNCiMgayA8LSBsZW5ndGgodW5pcXVlKHN1YnR5cGVzJFN1YnR5cGVfSW50ZWdyYXRpdmUpKQ0Kc3BlY3RyYWxfY2x1c3RlcmluZyA8LSBzcGVjdHJhbENsdXN0ZXJpbmcoV19pbnQsIGspDQpzdHIoc3BlY3RyYWxfY2x1c3RlcmluZykNCiMgVmlzdWFsaXp6YSBpIHJpc3VsdGF0aQ0KdGFibGUoc3BlY3RyYWxfY2x1c3RlcmluZykNCmBgYA0KYGBge3J9DQojIENyZWFyZSB1bmEgbGlzdGEgY29uIGkgcmlzdWx0YXRpIGRpIGNsdXN0ZXJpbmcNCmNsdXN0ZXJpbmdfcmVzdWx0cyA8LSBsaXN0KHBhbV9yZXNfbWlSTkEgPSBwYW1fcmVzX21pUk5BJGNsdXN0ZXJpbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwYW1fcmVzX21STkEgPSBwYW1fcmVzX21STkEkY2x1c3RlcmluZywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbV9yZXNfcHJvdGVpbnMgPSBwYW1fcmVzX3Byb3RlaW5zJGNsdXN0ZXJpbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwYW1fcmVzX2F2ZyA9IHBhbV9yZXNfYXZnJGNsdXN0ZXJpbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwYW1fcmVzX3NuZiA9IHBhbV9yZXNfc25mJGNsdXN0ZXJpbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzcGVjdHJhbF9jbHVzdGVyaW5nID0gc3BlY3RyYWxfY2x1c3RlcmluZykNCg0KIyBDYWxjb2xhcmUgbGEgdGFiZWxsYSBkaSBjb250aW5nZW56YSBwZXIgY2lhc2N1biByaXN1bHRhdG8gZGkgY2x1c3RlcmluZyByaXNwZXR0byBhaSBzb3R0b3RpcGkgZGkgbWFsYXR0aWUgaUNsdXN0ZXINCmZvciAobmFtZSBpbiBuYW1lcyhjbHVzdGVyaW5nX3Jlc3VsdHMpKSB7DQogIHByaW50KHBhc3RlKCJDb250aW5nZW5jeSB0YWJsZSBmb3IiLCBuYW1lKSkNCiAgcHJpbnQodGFibGUoc3VidHlwZXMkU3VidHlwZV9JbnRlZ3JhdGl2ZSwgY2x1c3RlcmluZ19yZXN1bHRzW1tuYW1lXV0pKQ0KfQ0KDQojIENhbGNvbGFyZSBsJ2luZGljZSBkaSBSYW5kIEFkanVzdGVkIHBlciBjaWFzY3VuIHJpc3VsdGF0byBkaSBjbHVzdGVyaW5nIHJpc3BldHRvIGFpIHNvdHRvdGlwaSBkaSBtYWxhdHRpZSBpQ2x1c3Rlcg0KZm9yIChuYW1lIGluIG5hbWVzKGNsdXN0ZXJpbmdfcmVzdWx0cykpIHsNCiAgcHJpbnQocGFzdGUoIkFkanVzdGVkIFJhbmQgaW5kZXggZm9yIiwgbmFtZSkpDQogIHByaW50KGNsdXN0ZXI6OmFkanVzdGVkUmFuZEluZGV4KGlDbHVzdGVyX3N1YnR5cGVzLCBjbHVzdGVyaW5nX3Jlc3VsdHNbW25hbWVdXSkpDQp9DQoNCiMgQ3JlYXJlIHVuIGdyYWZpY28gYSBiYXJyZSBwZXIgY2lhc2N1biByaXN1bHRhdG8gZGkgY2x1c3RlcmluZw0KcGFyKG1mcm93ID0gYygyLCAzKSkgICMgSW1wb3N0YXJlIGlsIGxheW91dCBkZWwgZ3JhZmljbw0KZm9yIChuYW1lIGluIG5hbWVzKGNsdXN0ZXJpbmdfcmVzdWx0cykpIHsNCiAgYmFycGxvdCh0YWJsZShjbHVzdGVyaW5nX3Jlc3VsdHNbW25hbWVdXSksIG1haW4gPSBuYW1lLCB4bGFiID0gIkNsdXN0ZXIiLCB5bGFiID0gIkNvdW50IikNCn0NCmBgYA0KDQoNCg0KUFVOVE8gMTE6DQpgYGB7cn0NCiMgQ3JlYXJlIHVuYSB0YWJlbGxhIGRpIGNvbmZyb250byBjb24gbm9taSBkZXNjcml0dGl2aSBwZXIgZ2xpIGFwcHJvY2NpDQpjb21wYXJpc29uX3RhYmxlIDwtIGRhdGEuZnJhbWUoDQogIE1ldGhvZCA9IGMoIlBBTV9taVJOQSIsICJQQU1fbVJOQSIsICJQQU1fcHJvdGVpbnMiLCAiUEFNX0F2ZyIsICJQQU1fU05GIiwgIlNwZWN0cmFsX0NsdXN0ZXJpbmciKSwNCiAgQ2x1c3Rlcl9Bc3NpZ25tZW50ID0gYygNCiAgICBwYW1fcmVzX21pUk5BJGNsdXN0ZXJpbmcsIA0KICAgIHBhbV9yZXNfbVJOQSRjbHVzdGVyaW5nLCANCiAgICBwYW1fcmVzX3Byb3RlaW5zJGNsdXN0ZXJpbmcsIA0KICAgIHBhbV9yZXNfYXZnJGNsdXN0ZXJpbmcsIA0KICAgIHBhbV9yZXNfc25mJGNsdXN0ZXJpbmcsIA0KICAgIHNwZWN0cmFsX2NsdXN0ZXJpbmcNCiAgKQ0KKQ0KDQojIFZpc3VhbGl6emFyZSBsYSB0YWJlbGxhDQpwcmludChjb21wYXJpc29uX3RhYmxlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhcmUgdW4gZ3JhZmljbyBhIGJhcnJlIHBlciBjb25mcm9udGFyZSBpIGNsdXN0ZXINCmJhcnBsb3QoDQogIHRhYmxlKGNvbXBhcmlzb25fdGFibGUkTWV0aG9kLCBjb21wYXJpc29uX3RhYmxlJENsdXN0ZXJfQXNzaWdubWVudCksDQogIGJlc2lkZSA9IFRSVUUsDQogIGNvbCA9IGMoImJsdWUiLCAicmVkIiwgImdyZWVuIiwgIm9yYW5nZSIsICJwdXJwbGUiLCAicGluayIpLA0KICBsZWdlbmQudGV4dCA9IFRSVUUsDQogIGFyZ3MubGVnZW5kID0gIGxpc3QoeCA9ICJ0b3ByaWdodCIsIGJ0eSA9ICJuIiksICAjIFBvc2l6aW9uYSBsYSBsZWdlbmRhIGluIGFsdG8gYSBkZXN0cmEgc2VuemEgYm94DQogIG1haW4gPSAiQ29uZnJvbnRvIGRlaSBjbHVzdGVyaW5nIiwNCiAgeGxhYiA9ICJNZXRvZG8iLA0KICB5bGFiID0gIkZyZXF1ZW56YSINCikNCmBgYA0KDQo=